package object;

import org.testng.annotations.Test;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

/**
 * 这是因为hashCode和equals没写好导致的内存泄漏，不是引用问题
 * 所以不建议把整个对象都存进去，只存储一个编号就行了
 *
 * Tips：如果非要存储对象类型，而且还需要修改，那么做法是先删除再修改，最后重新保存
 */
public class MemoryLeak {

    public static void main(String[] args){
        Set<Person> set = new HashSet<>();
        Person p1 = new Person( "唐僧" , "pwd1" , 25 );
        Person p2 = new Person( "孙悟空" , "pwd2" , 26 );
        Person p3 = new Person( "猪八戒" , "pwd3" , 27 );

        set.add(p1);
        set.add(p2);
        set.add(p3);

        // 结果：总共有:3 个元素!
        System.out.println( "总共有:" +set.size()+ " 个元素!" );

        //只要重写了hashCode方法，修改任何一个属性都会导致对象的hashCode值改变
//        p3.setAge(2); //修改p3的年龄,此时p3元素对应的hashcode值发生改变
//        p3.setName("jjj");
          p3.setPassword("a5s");
//        set.add(p3);

        //此时remove不掉，造成内存泄漏，因为p3修改后，hashMap的tab计算的下标跟原来的不一样了，tab删除元素不是通过遍历比较，为了效率是直接计算出位置，
        // 计算出的位置的元素和要删除的不一样，就删不掉了
        System.out.println("remove p3: " + set.remove(p3));

        System.out.println("add p3: " + set.add(p3)); //重新添加，居然添加成功

        System.out.println( "总共有:" +set.size()+ " 个元素!" ); //结果：总共有:4 个元素!

        for (Person person : set) {
            System.out.println(person);
        }
    }

    @Test
    public void t2() {
        Set<PersonReHash> set = new HashSet<>();
        PersonReHash p1 = new PersonReHash(1, "唐僧" , "pwd1" , 25 );
        PersonReHash p2 = new PersonReHash(2, "孙悟空" , "pwd2" , 26 );
        PersonReHash p3 = new PersonReHash(3, "猪八戒" , "pwd3" , 27 );

        set.add(p1);
        set.add(p2);
        set.add(p3);

        System.out.println( "总共有:" +set.size()+ " 个元素!" ); // 3

        // 修改password不影响hashCode，计算出的位置的元素和要删除的一样
        p3.setPassword("a5s");
        System.out.println("remove p3: " + set.remove(p3));

        System.out.println("add p3: " + set.add(p3)); // 重新添加成功

        System.out.println( "总共有:" +set.size()+ " 个元素!" ); //结果：总共有:3 个元素!

        for (PersonReHash person : set) {
            System.out.println(person);
        }
    }


}
class Person{
    private String name;
    private String password;
    private int age;

    public Person(String name, String password, int age) {
        this.name = name;
        this.password = password;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", age=" + age +
                '}';
    }

    //必须重写hashCode方法才能保证remove删不掉元素
    //因为重写后，值修改后会重新调用此方法计算hashcode
    @Override
    public int hashCode() {
        // int result = name != null ? name.hashCode() : 0;
        // result = 31 * result + (password != null ? password.hashCode() : 0);
        // result = 31 * result + age;
        // return result;

        // 更简单的hashCode方案，还是没法删除属性相同的元素，没有办法？
        return Objects.hash(name, password, age);
    }

    public String getName() {
        return name;
    }

    public String getPassword() {
        return password;
    }

    public int getAge() {
        return age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setAge(int age) {
        this.age = age;
    }


}

class PersonReHash {

    private final int id;

    private String name;
    private String password;
    private int age;

    public PersonReHash(int id, String name, String password, int age) {
        this.id = id;
        this.name = name;
        this.password = password;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", age=" + age +
                '}';
    }

    //必须重写hashCode方法才能保证remove删不掉元素
    //因为重写后，值修改后会重新调用此方法计算hashcode
    @Override
    public int hashCode() {
        // int result = name != null ? name.hashCode() : 0;
        // result = 31 * result + (password != null ? password.hashCode() : 0);
        // result = 31 * result + age;
        // return result;

        //
        return id;
    }

    public String getName() {
        return name;
    }

    public String getPassword() {
        return password;
    }

    public int getAge() {
        return age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setAge(int age) {
        this.age = age;
    }


}
